package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.service.HotArticleService;
import com.heima.common.exception.CustException;
import com.heima.common.exception.CustomException;
import com.heima.feigns.AdminFeign;
import com.heima.model.admin.pojos.AdChannel;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.article.vos.HotArticleVo;
import com.heima.model.common.constants.article.ArticleConstants;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.mess.app.AggBehaviorDTO;
import com.heima.utils.common.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Slf4j
public class HotArticleServiceImpl implements HotArticleService {

    @Autowired
    private ApArticleMapper apArticleMapper;

    @Autowired
    private AdminFeign adminFeign;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public void computeHotArticle() {
        //1. 基于当前时间,擦互训近五天得文章数据
        // 获取5天之前得时间
        String format = LocalDateTime.now().minusDays(5).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        List<ApArticle> articleList = apArticleMapper.selectArticleByDate(format);
        if (CollectionUtils.isEmpty(articleList)) {
            log.info("此app已黄了,没人发表文章.........");
            return;
        }

        //2.遍历文章列表,计算文章热度分值
        List<HotArticleVo> hotArticleVoList = getHotArticleVoList(articleList);

        //3.按照频道缓存热点文章

        cacheRedisByTag(hotArticleVoList);
    }

    @Override
    public void updateApArticle(AggBehaviorDTO aggBehavior) {
        //1. 根据文章id查询文章对象
        ApArticle article = apArticleMapper.selectById(aggBehavior.getArticleId());
        if (article == null) {
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST, "文章不存在");

        }
        //2.根据行为聚合结果,修改文章个行为的统计值
        article.setViews((int) (article.getViews() + aggBehavior.getView()));
        article.setViews((int) (article.getLikes() + aggBehavior.getLike()));
        article.setViews((int) (article.getComment() + aggBehavior.getComment()));
        article.setViews((int) (article.getCollection() + aggBehavior.getCollect()));
        apArticleMapper.updateById(article);

        //3.重新计算文章热度得分
        Integer score = computScore(article);
        //4.判断是否当日发布文章 如果是 整体热度*3
        String now = DateUtils.dateToString(new Date());
        String publish = DateUtils.dateToString(article.getPublishTime());
        if (now.equals(publish)) {
            score = score * 3;
        }
        //5.  TODO 跟新所在频道 热点文章缓存
        updateApArticleCache(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + article.getChannelId(), article, score);
        //6. TODO  跟新推荐频道 热点文章缓存
        updateApArticleCache(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + ArticleConstants.DEFAULT_TAG, article, score);
    }

    /**
     * @param s       缓存的key
     * @param article 跟信万热度的文章对象
     * @param score   最新的热度值
     */
    private void updateApArticleCache(String s, ApArticle article, Integer score) {
        //1. 根据缓存的key 查询对应 缓存列表
        String jsonStr = redisTemplate.opsForValue().get(s);
        List<HotArticleVo> hotArticleVoList = JSON.parseArray(jsonStr, HotArticleVo.class);
        //2. 判断当前文章是否是热点文章
        //如果是 直接跟新文章热度值
        boolean flag = false;
        for (HotArticleVo articleVo : hotArticleVoList) {
            if (articleVo.getId().equals(article.getId())) {
                //如果是   直接更新文章热读值
                articleVo.setScore(score);
                flag = true;
                break;
            }
        }

        //3. 如果没有存在热带文章中
        if (!flag){
            //将单枪文章直接加入到热点文章集合中
            HotArticleVo hotArticleVo = new HotArticleVo();
            BeanUtils.copyProperties(article,hotArticleVo);
            hotArticleVo.setScore(score);
            hotArticleVoList.add(hotArticleVo);
        }


        //4. 将文张 按照热度降序排休,并且截取前30条

        hotArticleVoList    =  hotArticleVoList.stream()
                .sorted(Comparator.comparing(HotArticleVo::getScore).reversed()).limit(30).collect(Collectors.toList());
        //5.将最新热点文章缓存到redis ,跟新热点文章缓存

        redisTemplate.opsForValue().set(s,JSON.toJSONString(hotArticleVoList));
    }

    /**
     * 将文章 按照频道 缓存到redis中
     *
     * @param hotArticleVoList
     */
    private void cacheRedisByTag(List<HotArticleVo> hotArticleVoList) {
        //1.远程查询频道列表
        ResponseResult<List<AdChannel>> result = adminFeign.selectChannels();
        if (!result.checkCode()) {
            CustException.cust(AppHttpCodeEnum.REMOTE_SERVER_ERROR);
        }
        List<AdChannel> channelList = result.getData();

        //2.为每个频道缓存文章
        for (AdChannel adChannel : channelList) {
            List<HotArticleVo> collect = hotArticleVoList.stream()
                    .filter(hotArticleVo -> hotArticleVo.getChannelId().equals(adChannel.getId()))
                    .collect(Collectors.toList());
            //缓存文章
            sortAndCache(collect, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + adChannel.getId());
        }

        //3.为推荐频道缓存文章
        sortAndCache(hotArticleVoList, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + ArticleConstants.DEFAULT_TAG);

    }

    /**
     * 把热门文章缓存到redis
     *
     * @param collect
     * @param s
     */
    private void sortAndCache(List<HotArticleVo> collect, String s) {
        //1. String 结构  List<article> ==> [{文章1},{文章2}]

        //2. 将文章按照热度降序排序 截取前30条
        List<HotArticleVo> hotArticleVoList = collect.stream()
                //按照文章热度降序排序
                .sorted(Comparator.comparing(HotArticleVo::getScore).reversed())
                //截取前30条
                .limit(30)
                .collect(Collectors.toList());
        //1. String 结构  List<article> ==> [{文章1},{文章2}]
        redisTemplate.opsForValue().set(s, JSON.toJSONString(hotArticleVoList));


    }

    /**
     * 计算每篇文章分值
     *
     * @param articleList
     * @return
     */
    private List<HotArticleVo> getHotArticleVoList(List<ApArticle> articleList) {
        //1.遍历文章
        return articleList.stream()
                .map(apArticle -> {
                    HotArticleVo hotArticleVo = new HotArticleVo();
                    //计算每篇文章的分值
                    BeanUtils.copyProperties(apArticle, hotArticleVo);
                    hotArticleVo.setScore(computScore(apArticle));
                    return hotArticleVo;
                }).collect(Collectors.toList());

        //2. 计算每篇文章的分值


        //3.将article封装为vo对象


    }

    /**
     * 计算每篇文章的热度得分
     *
     * @param apArticle
     * @return
     */
    private Integer computScore(ApArticle apArticle) {

        Integer score = 0;
        Integer views = apArticle.getViews();
        // 阅读 1
        if (views != null) {
            score += views * ArticleConstants.HOT_ARTICLE_VIEW_WEIGHT;
        }
        // 点赞 3
        if (apArticle.getLikes() != null) {
            score += apArticle.getLikes() * ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;
        }
        // 评论 5
        if (apArticle.getComment() != null) {
            score += apArticle.getComment() * ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
        }
        // 收藏 8
        if (apArticle.getCollection() != null) {
            score += apArticle.getCollection() * ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
        }
        return score;
    }
}
